package sf.database.jdbc.extension;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import sf.common.exception.SmallOrmException;
import sf.database.dialect.DBDialect;
import sf.database.jdbc.type.TypeHandler;
import sf.database.meta.ColumnMapping;
import sf.spring.util.Assert;
import sf.tools.GenericsUtils;
import sf.tools.StringUtils;

import java.io.IOException;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

/**
 * jackson实现的json映射.
 * @param <T>
 */
public class ObjectJacksonMapping<T> implements TypeHandler<T> {
    private Class<T> clazz = (Class<T>) Object.class;
    private Type genericType;
    private Class<?>[] genericCls;

    public static ObjectMapper mapper = new ObjectMapper();
    private JavaType javaType;

    @Override
    public Class<T> getDefaultJavaType() {
        return clazz;
    }

    @Override
    public void setDefaultJavaType(Class<T> clz) {
        Assert.notNull(clz, "自定义类型不能为空");
        clazz = clz;
    }

    @Override
    public int getSqlType() {
        return Types.CLOB;
    }

    @Override
    public void setColumnMapping(ColumnMapping columnMapping) {
        this.genericType = columnMapping.getFieldAccessor().getGenericType();
        this.clazz = (Class<T>) columnMapping.getClz();
        if (genericType != null) {
            genericCls = GenericsUtils.getGenericTypeClasses(genericType);
            if (genericCls != null) {
                javaType = mapper.getTypeFactory().constructParametricType(clazz, genericCls);
            }
        }
    }

    @Override
    public T get(ResultSet rs, int n) throws SQLException {
        String o = rs.getString(n);
        if (StringUtils.isEmpty(o))
            return null;
        return getObject(o);
    }

    @Override
    public T get(ResultSet rs, String columnName) throws SQLException {
        String o = rs.getString(columnName);
        if (StringUtils.isEmpty(o))
            return null;
        return getObject(o);
    }

    @Override
    public T get(CallableStatement cs, int index) throws SQLException {
        String o = cs.getString(index);
        if (StringUtils.isEmpty(o))
            return null;
        return getObject(o);
    }

    @Override
    public T get(CallableStatement cs, String parameterName) throws SQLException {
        String o = cs.getString(parameterName);
        if (StringUtils.isEmpty(o))
            return null;
        return getObject(o);
    }

    private T getObject(String o) {
        try {
            if (javaType != null) {
                return mapper.readValue(o, javaType);
            } else {
                return mapper.readValue(o, clazz);
            }
        } catch (IOException e) {
            throw new SmallOrmException(e);
        }
    }

    @Override
    public void update(ResultSet rs, String columnLabel, Object value) throws SQLException {
        try {
            String s = mapper.writeValueAsString(value);
            rs.updateString(columnLabel, s);
        } catch (JsonProcessingException e) {
            throw new SmallOrmException(e);
        }
    }

    @Override
    public void update(ResultSet rs, int columnIndex, Object value) throws SQLException {
        String s = toJson(value);
        rs.updateString(columnIndex, s);
    }

    @Override
    public Object set(PreparedStatement ps, Object obj, int index) throws SQLException {
        if (obj == null) {
            ps.setNull(index, getSqlType());
        } else {
            String s = toJson(obj);
            ps.setString(index, s);
        }
        return null;
    }

    @Override
    public Object set(CallableStatement cs, Object obj, String parameterName) throws SQLException {
        if (obj == null) {
            cs.setNull(parameterName, getSqlType());
        } else {
            String s = toJson(obj);
            cs.setString(parameterName, s);
        }
        return null;
    }

    protected String toJson(Object obj) {
        if (obj instanceof String) {
            return (String) obj;
        }
        try {
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            throw new SmallOrmException(e);
        }
    }

    @Override
    public String getSqlExpression(Object value, DBDialect profile) {
        String s = toJson(value);
        return wrapSqlStr(s);
    }

    /**
     * 用单引号包围字符串，并将其中的单引号按SQL转义
     * @param s
     * @return
     */
    public static String wrapSqlStr(String s) {
        StringBuilder sb = new StringBuilder(s.length() + 16);
        sb.append('\'');
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == '\'') {
                sb.append("''");
            } else {
                sb.append(c);
            }
        }
        sb.append('\'');
        return sb.toString();
    }
}